﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Baik.Interface.Execution;
using Baik.Interface.Framework;

namespace Baik.Execution
{
    public class Array : BuiltInObject, IArray
    {
        public Array(IInjector inject) : base(inject)
        {
            ArrayDictionary = new Dictionary<int, IObject>();
            Dictionary.Add("add",  BuiltInFactory.Function(inject, Add));
            Dictionary.Add("remove",  BuiltInFactory.Function(inject, Remove));
            Dictionary.Add("contains",  BuiltInFactory.Function(inject, Contains));
        }
        private IObject Contains(IEnumerable<IObject> arg)
        {
            var rtn = false;
            foreach (var obj in arg)
            {
                
                if (obj is IString)
                {
                    rtn = ArrayDictionary.Select(kvp => kvp.Value).OfType<IString>().Any(v => v.Value == ((IString) obj).Value);
                    if (rtn == false) return Inject.Instantiate<IFalse>();
                }
                else if (obj is INumber)
                {
                    rtn = ArrayDictionary.Select(kvp => kvp.Value).OfType<INumber>().Any(v => Math.Abs(v.Value - ((INumber)obj).Value) < 0.000001);
                    if (rtn == false) return Inject.Instantiate<IFalse>();
                }
                else if (obj is IArray)
                {
                    var arr = (IArray) obj;
                    if (!((IBoolean)Contains(arr.Items)).Value) return Inject.Instantiate<IFalse>();
                }
                else
                {
                    rtn = ArrayDictionary.Select(kvp => kvp.Value).Any(v => v.Equals(obj));
                    if (rtn == false) return Inject.Instantiate<IFalse>();
                }
                

            }
            return Inject.Instantiate<ITrue>();
        }

        private IObject Remove(IEnumerable<IObject> arg)
        {
            arg.OfType<INumber>().ToList().ForEach(n => Remove(Convert.ToInt32(n.Value)));
            return this;
        }

        private IObject Add(IEnumerable<IObject> arg)
        {
            arg.ToList().ForEach(Add);
            return this;
        }

        private string pstring()
        {
            var sw = new StringWriter();
            for (int i = 0; i <= HighestKey; i++)
            {
                if (i > 0) sw.Write(", ");
                sw.Write(this[i].AsString());
            }
            return sw.ToString();
        }
        public override string AsString()
        {
            return "[" + pstring() + "]";
        }

        private int HighestKey = -1;
        public Dictionary<int, IObject> ArrayDictionary { get; private set; }
        public IObject this [int index]
        {
            get
            {
                IObject rtn = null;
                if (!ArrayDictionary.TryGetValue(index, out rtn)) rtn = Inject.Instantiate<IUndefined>();
                return rtn;
            }
            set
            {
                HighestKey = HighestKey < index ? index : HighestKey;
                ArrayDictionary.Add(index, value);
            }
        }
        public IEnumerable<IObject> Items {
            get { return ArrayDictionary.Select(kvp => kvp.Value).ToList(); }
            set
            {
                int i = 0;
                ArrayDictionary.Clear();
                (value as IObject[]  ?? value.ToArray()).ToList().ForEach(itm => ArrayDictionary.Add(i++, itm));
            }
        }

        public IObject GetByIndex(double index)
        {
            IObject rtn = null;
            return ArrayDictionary.TryGetValue(Convert.ToInt32(index), out rtn) ? rtn : Inject.Instantiate<IUndefined>();
        }

        public void Add(IObject obj)
        {
            this[++HighestKey] = obj;
        }
        public void Remove(int index)
        {
            ArrayDictionary.Remove(index);
        }
    }
}